/**
    [Filename]
    Purpose: [Purpose]

    @author Jason Wojcik
    @version [version]
*/

#include "SDL/SDL.h"    //include SDL functions and data types
#include "SDL/SDL_image.h"  //image file
#include "SDL/SDL_ttf.h"
#include "SDL/SDL_mixer.h"
#include <string>
#include <sstream>


//screen attributes
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;

//frame rate
const int FRAMES_PER_SECOND = 20;

//The button states in the button sprite sheet
const int CLIP_MOUSEOVER = 0;
const int CLIP_MOUSEOUT = 1;
const int CLIP_MOUSEDOWN = 2;
const int CLIP_MOUSEUP = 3;

//dot width dimentions
const int DOT_HEIGHT = 20;
const int DOT_WIDTH = 20;
const int DOT_VEL = 3;
const int TOTAL_PARTICLES = 5;

//level dimensions
const int LEVEL_WIDTH = 640;
const int LEVEL_HEIGHT = 480;

//Music and Sound
Mix_Music *music = NULL;
Mix_Chunk *scratch = NULL;
Mix_Chunk *high = NULL;
Mix_Chunk *med = NULL;
Mix_Chunk *low = NULL;

//surfaces
SDL_Surface *background = NULL;
SDL_Surface *message = NULL;
SDL_Surface *upMessage = NULL;
SDL_Surface *downMessage = NULL;
SDL_Surface *leftMessage = NULL;
SDL_Surface *rightMessage = NULL;
//SDL_Surface *foo = NULL;
SDL_Surface *dots = NULL;
SDL_Surface *screen = NULL;
SDL_Surface *buttonSheet = NULL;
SDL_Surface *seconds = NULL;
SDL_Surface *dot = NULL;
SDL_Surface *front = NULL;
SDL_Surface *red = NULL;
SDL_Surface *green = NULL;
SDL_Surface *blue = NULL;
SDL_Surface *shimmer = NULL;


SDL_Event event;    //event structure

TTF_Font *font = NULL;  //font

SDL_Color textColor = {0,0,255};    //color for text

SDL_Rect clips[4];   //clip rect for button
SDL_Rect clip[4];   //clip rect for dots

//camera rect
SDL_Rect camera = {0,0,SCREEN_WIDTH,SCREEN_HEIGHT};

//background ofsets
int bgX = 0, bgY = 0;

//Class particles
class Particle{
    private:
    //offsets
    int x, y;

    //curent frame of animation
    int frame;

    SDL_Surface *type;

    public:
    //Constructor
    Particle(int X, int Y);

    //show particle;
    void show();

    //checks if particle is dead
    bool is_dead();
};

Particle::Particle(int X, int Y){

    x = X - 5 + (rand() % 25);
    y = Y - 5 + (rand() % 25);

    frame = rand() % 5;

    switch( rand() %3){
        case 0: type = red; break;
        case 1: type = green; break;
        case 2: type = blue; break;
        default: break;
    }

}

//Window Class
class Window{

    private:
    bool windowed;
    bool windowOK;

    public:
    Window();
    void handle_events();
    void toggle_fullscreen();
    bool error();

};

//Window constructor
Window::Window(){

    //set up screen
    screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT,
        SCREEN_BPP,SDL_SWSURFACE | SDL_RESIZABLE);

    //if there is an error
    if(screen == NULL){
        windowOK = false;
        return;
    }
    else windowOK = true;

    SDL_WM_SetCaption("Window Event Test", NULL);

    //set windowed flag
    windowed = true;
}

//Window class event handler
void Window::handle_events(){

    if(!windowOK) return;

    if(event.type == SDL_VIDEORESIZE){
        screen = SDL_SetVideoMode(event.resize.w, event.resize.h,
            SCREEN_BPP,SDL_SWSURFACE | SDL_RESIZABLE);
        if(screen == NULL){
            windowOK = false;
            return;
        }
    }
    //if 'F' pressed
    else if ((event.type == SDL_KEYDOWN) && (event.key.keysym.sym == SDLK_RETURN)){
        toggle_fullscreen();
        }
    //If the window focus changes
    else if(event.type == SDL_ACTIVEEVENT){
        if(event.active.state && SDL_APPACTIVE){
            if(event.active.gain == 0){
                SDL_WM_SetCaption("ICON",NULL);
            }else{
                SDL_WM_SetCaption("WINDOWED", NULL);
            }
        }
    }
    //If something happened to the keyboard focus
    else if(event.active.state && SDL_APPINPUTFOCUS){
            if(event.active.gain == 0){
                SDL_WM_SetCaption("KEY LOST",NULL);
            }else{
                SDL_WM_SetCaption("WINDOW EVENT TEST", NULL);
            }
    }
    //If somethign happened to mouse focus
    else if(event.active.state && SDL_APPMOUSEFOCUS){
            if(event.active.gain == 0){
                SDL_WM_SetCaption("MOUSE LOST",NULL);
            }else{
                SDL_WM_SetCaption("WINDOW EVENT TEST", NULL);
            }
    }
    //If the window screen has been altered
    else if(event.type == SDL_VIDEOEXPOSE){
        if(SDL_Flip(screen) == -1){
            windowOK = false;
            return;
        }
    }

}

//Window toggle fullscreen
void Window::toggle_fullscreen(){
    //If the screen is windowed
    if(windowed){
        //set fullscreen
        screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT,
            SCREEN_BPP ,SDL_SWSURFACE | SDL_RESIZABLE | SDL_FULLSCREEN);
        if(screen == NULL){
            windowOK = false;
            return;
        }
        windowed = false;
    }
    //If the screen is full screen
    else if(!windowed){
        //set fullscreen
        screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT,
            SCREEN_BPP,SDL_SWSURFACE | SDL_RESIZABLE);
        if(screen == NULL){
            windowOK = false;
            return;
        }
        windowed = true;
    }
}

//Window error
bool Window::error(){
    return !windowOK;
}

//Dot Class
class Dot{
    private:
    float x,y;
    float dx, dy;

    //The particles
    Particle *particles[TOTAL_PARTICLES];

    public:
    Dot();
    ~Dot();
    void handle_input();
    void move( Uint32 deltaTicks);
    void show_particles();
    void show();
    void set_camera(); //sets the camera over the dot
};

//Dot Contstructor
Dot::Dot(){
    x = 310;
    y = 230;
    dx = 0;
    dy = 0;

    //initalize particles
    for(int p = 0; p< TOTAL_PARTICLES; p++){
        particles[p] = new Particle(x,y);
    }
}

Dot::~Dot(){

    for(int p = 0; p < TOTAL_PARTICLES; p++){
        delete particles[p];
    }

}

//handle dot input
void Dot::handle_input(){
    if(event.type == SDL_KEYDOWN){
        switch(event.key.keysym.sym){
            case SDLK_UP: dy -= DOT_VEL; break;
            case SDLK_DOWN: dy += DOT_VEL; break;
            case SDLK_LEFT: dx -= DOT_VEL; break;
            case SDLK_RIGHT: dx += DOT_VEL; break;
            default: break;
        }
    }
     //If a key was released
    else if( event.type == SDL_KEYUP )
    {
        //Adjust the velocity
        switch( event.key.keysym.sym )
        {
            case SDLK_UP: dy += DOT_VEL; break;
            case SDLK_DOWN: dy -= DOT_VEL; break;
            case SDLK_LEFT: dx += DOT_VEL; break;
            case SDLK_RIGHT: dx -= DOT_VEL; break;
            default: break;
        }
    }
}

void Dot::set_camera(){
    //center the camera over the dot
    camera.x = (x+ DOT_WIDTH /2) - SCREEN_WIDTH / 2;
    camera.y = (y+ DOT_HEIGHT /2) - SCREEN_HEIGHT / 2;

    //keep the camera in bounds
    if(camera.x < 0) camera.x = 0;
    if(camera.y < 0) camera.y = 0;
    if(camera.x > LEVEL_WIDTH - camera.w) camera.x = LEVEL_WIDTH - camera.w;
    if(camera.y > LEVEL_HEIGHT- camera.h) camera.y = LEVEL_HEIGHT - camera.h;
}

//move the dot
void Dot::move(Uint32 deltaTicks){
    x += dx * (deltaTicks / 1000.f);
    if((x<0) || (x + DOT_WIDTH > LEVEL_WIDTH)) x -= dx * (deltaTicks / 1000.f);
    y += dy * (deltaTicks / 1000.f);
    if((y<0) || (y + DOT_HEIGHT > LEVEL_HEIGHT)) y -= dy * (deltaTicks / 1000.f);
}

//Timer Class
class Timer{
    private:
    //The clock time when the timer started
    int startTicks;

    //the ticks stored when the timer is paused
    int pausedTicks;

    //The timer status
    bool paused;
    bool started;

    public:
    //Initialize
    Timer();

    //Clock methods
    void start();
    void stop();
    void pause();
    void unpause();

    //get timer's time
    int get_ticks();

    //Checks status of timer
    bool is_started();
    bool is_paused();
};

//Timer Constructor
Timer::Timer(){
    startTicks = 0;
    pausedTicks = 0;
    paused = false;
    started = false;
}

//Starts the Timer
void Timer::start(){
    started = true;
    paused = false;
    startTicks = SDL_GetTicks();
}

//stops the timer
void Timer::stop(){
    started = false;
    paused = false;
}

//get the ticks
int Timer::get_ticks(){
    if(started){
        if(paused) return pausedTicks;
        else return SDL_GetTicks() - startTicks;
    }
    return 0;
}

//pause the timer and collect ticks in pausedTicks
void Timer::pause(){
    if (started && !paused){
        paused = true;
        pausedTicks = SDL_GetTicks() - startTicks;
    }
}

//unpauses the timer, continues start Ticks and resets pausedTicks
void Timer::unpause(){
    if(paused){
        paused = false;
        startTicks = SDL_GetTicks() - pausedTicks;
        pausedTicks = 0;
    }
}

//returns true if timer is started
bool Timer::is_started(){
    return started;
}

//returns true if timer is paused
bool Timer::is_paused(){
    return paused;
}

//The button
class Button{
    private:
    SDL_Rect box;
    SDL_Rect* clip;

    public:
    Button(int x, int y, int w, int h);

    void handle_events();

    void show();
};

//Button constructor
Button::Button(int x, int y, int w, int h){
    box.x = x;
    box.y = y;
    box.w = w;
    box.h = h;

    clip = &clips[ CLIP_MOUSEOUT ];
}

//Clip rect for the Mouse button
void set_clips(){
    //Clip the sprite sheet
    clips[ CLIP_MOUSEOVER ].x = 0;
    clips[ CLIP_MOUSEOVER ].y = 0;
    clips[ CLIP_MOUSEOVER ].w = 320;
    clips[ CLIP_MOUSEOVER ].h = 240;

    clips[ CLIP_MOUSEOUT ].x = 320;
    clips[ CLIP_MOUSEOUT ].y = 0;
    clips[ CLIP_MOUSEOUT ].w = 320;
    clips[ CLIP_MOUSEOUT ].h = 240;

    clips[ CLIP_MOUSEDOWN ].x = 0;
    clips[ CLIP_MOUSEDOWN ].y = 240;
    clips[ CLIP_MOUSEDOWN ].w = 320;
    clips[ CLIP_MOUSEDOWN ].h = 240;

    clips[ CLIP_MOUSEUP ].x = 320;
    clips[ CLIP_MOUSEUP ].y = 240;
    clips[ CLIP_MOUSEUP ].w = 320;
    clips[ CLIP_MOUSEUP ].h = 240;
}

//applies surfaces at x,y
void apply_surface(int x, int y, SDL_Surface* source,
    SDL_Surface* destination, SDL_Rect* clip = NULL){
    //Make a temporary rectangle to the hold the offsets
    SDL_Rect offset;

    //Give the offsets to the rectangles
    offset.x = x;
    offset.y = y;

    //Blit the surface
    SDL_BlitSurface(source, clip, destination, &offset);
}

//Show the button
void Button::show(){
    apply_surface(box.x, box.y, buttonSheet, screen, clip);
}

//show the dot
void Dot::show(){
    apply_surface((int)x - camera.x,(int)y - camera.y,dot,screen);

    show_particles();
}

void Dot::show_particles(){
    for(int p = 0; p < TOTAL_PARTICLES; p++){
        if(particles[p]->is_dead()){
            delete particles[p];
            particles[p] = new Particle(x,y);
        }
    }

    for(int p = 0; p< TOTAL_PARTICLES; p++){
        particles[p]->show();
    }
}

//show the particle
void Particle::show(){
    apply_surface( x,y, type, screen);

    if(frame %2 == 0)apply_surface(x,y, shimmer, screen);

    //animate
    frame ++;

}

//particle is dead?
bool Particle::is_dead(){
    if (frame > 10) return true;
    return false;
}

//event handler for the button
void Button::handle_events(){
    int x = 0, y = 0;

    if(event.type == SDL_MOUSEMOTION){
        x = event.motion.x;
        y = event.motion.y;

        if((x>box.x)&& (x < box.x + box.w) && (y > box.y) && (y < box.y + box.h )){
            clip = &clips[CLIP_MOUSEOVER];
        }
        else{
            clip = &clips[CLIP_MOUSEOUT];
        }
    }

            //if a mouse button pressed
        if(event.type == SDL_MOUSEBUTTONDOWN){

            //Left
            if(event.button.button == SDL_BUTTON_LEFT){
                x = event.button.x;
                y = event.button.y;

                if((x>box.x)&& (x < box.x + box.w) && (y > box.y) &&
                   (y < box.y + box.h )){
                    clip = &clips[CLIP_MOUSEDOWN];
                }
            }
        }

            //If a mouse button was released
    if( event.type == SDL_MOUSEBUTTONUP )
    {
        //If the left mouse button was released
        if( event.button.button == SDL_BUTTON_LEFT )
        {
            //Get the mouse offsets
            x = event.button.x;
            y = event.button.y;

            //If the mouse is over the button
            if( ( x > box.x ) && ( x < box.x + box.w ) && ( y > box.y ) && ( y < box.y + box.h ) )
            {
                clip = &clips[ CLIP_MOUSEUP ];  //Set the button sprite
            }
        }
    }
}

//Initialize SDL, sound, etc
bool init(){
    //Initialize SDL
    if (SDL_Init( SDL_INIT_EVERYTHING ) == -1) return 1;

    //font
    if (TTF_Init() == -1) return false;

    //sound
    if(Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT,2,4096) == -1) return false;

    //SDL_WM_SetCaption( "Caption", NULL);    //Set up the window Caption

    //Seed random
    srand(SDL_GetTicks());

    return true;
}

//Returns an optimized image, takes an image filename
SDL_Surface *load_image( std:: string filename ){
    //temp storage for the loaded image
    SDL_Surface* loadedImage = NULL;

    //The optimized image used
    SDL_Surface* optimizedImage = NULL;

    //Load the image
    loadedImage = IMG_Load( filename.c_str());

    if (loadedImage != NULL){
        //Create optimized image
        optimizedImage = SDL_DisplayFormat(loadedImage);

        //Free the old image
        SDL_FreeSurface(loadedImage);

        if(optimizedImage != NULL){
            Uint32 colorkey = SDL_MapRGB(optimizedImage->format, 0,0xFF,0xFF);
            SDL_SetColorKey(optimizedImage,SDL_SRCCOLORKEY,colorkey);
        }
    }
    return optimizedImage;
}

//load the files
bool load_files(){

    //load dots
    dots = load_image("dots.png");

    //load dot
    dot = load_image("dot.bmp");

    //load buttonSheet
    buttonSheet = load_image("button.png");

    //load particles
    red = load_image("red.bmp");
    green = load_image("green.bmp");
    blue = load_image("blue.bmp");
    shimmer = load_image("shimmer.bmp");

    //load background
    background = load_image( "bg.png" );

    //load the front
    front = load_image("fadeout.png");

    //load the font
    font = TTF_OpenFont("lazy.ttf", 28);
    if(font == NULL) return false;

    //load the music
    music = Mix_LoadMUS("beat.wav");
    if(music == NULL) return false;

    //load the sound effects
    scratch = Mix_LoadWAV("scratch.wav");
    high = Mix_LoadWAV("high.wav");
    med = Mix_LoadWAV("medium.wav");
    low = Mix_LoadWAV("low.wav");
    if ((scratch == NULL) || (high == NULL) || (med == NULL) || (low == NULL)){
        return false;
    }

    //foo = load_image( "foo.png" );
    //if(foo == NULL) return false;

    return true;
}

//cleanup
void clean_up(){
    //Clean up images
    //    SDL_FreeSurface(foo);
    SDL_FreeSurface(background);
    SDL_FreeSurface(message);
    SDL_FreeSurface( upMessage );
    SDL_FreeSurface( downMessage );
    SDL_FreeSurface( leftMessage );
    SDL_FreeSurface( rightMessage );
    SDL_FreeSurface(buttonSheet);
    SDL_FreeSurface(front);
    SDL_FreeSurface(red);
    SDL_FreeSurface(blue);
    SDL_FreeSurface(green);
    SDL_FreeSurface(shimmer);

    //Clean Music and Sound
    Mix_FreeChunk(scratch);
    Mix_FreeChunk(high);
    Mix_FreeChunk(med);
    Mix_FreeChunk(low);
    Mix_FreeMusic(music);
    Mix_CloseAudio();

    //clean font
    TTF_CloseFont(font);
    TTF_Quit();

    SDL_Quit();    //Quit SDL
}

/* OLD MAIN FUNCTION
int main( int argc, char* args[] ){
    bool quit = false;

    int alpha = SDL_ALPHA_OPAQUE;

    int frame = 0;  //current frame;
    bool cap = false; //frame rate capped
    Timer fps;  //frame rate regulator
    Timer update;   //update caption

    //initialize everything
    if( init() == false) return 1;

    //create the window
    Window myWindow;
    if(myWindow.error() == true) return 1;

    //load all the files
    if( load_files() == false) return 1;

    //Clip range for the top left
    clip[ 0 ].x = 0;
    clip[ 0 ].y = 0;
    clip[ 0 ].w = 100;
    clip[ 0 ].h = 100;

    //Clip range for the top right
    clip[ 1 ].x = 100;
    clip[ 1 ].y = 0;
    clip[ 1 ].w = 100;
    clip[ 1 ].h = 100;

    //Clip range for the bottom left
    clip[ 2 ].x = 0;
    clip[ 2 ].y = 100;
    clip[ 2 ].w = 100;
    clip[ 2 ].h = 100;

    //Clip range for the bottom right
    clip[ 3 ].x = 100;
    clip[ 3 ].y = 100;
    clip[ 3 ].w = 100;
    clip[ 3 ].h = 100;

    //Render the Hello Noah text
    //message = TTF_RenderText_Solid(font, "Hello Noah", textColor);
    //if(message == NULL) return 1;
    //apply_surface( 150, 250, message, screen );

    //clip the sprite sheet
    set_clips();

    //Make the button
    Button myButton(170,120,320,240);
    Dot myDot;

    //Make the timer
    Timer myTimer;
    myTimer.start();

    //Render other text
    upMessage = TTF_RenderText_Solid( font, "Up.", textColor );
    downMessage = TTF_RenderText_Solid( font, "Down.", textColor );
    leftMessage = TTF_RenderText_Solid( font, "Left", textColor );
    rightMessage = TTF_RenderText_Solid( font, "Right", textColor );

    //GAME LOOP
    while(quit == false){

        fps.start();    //start the frame timer
        update.start();

    //EVENTS
        while(SDL_PollEvent(&event)){

            //handle button events
            myWindow.handle_events();
            myButton.handle_events();
            myDot.handle_input();

            //handle keystroke events
            if(event.type == SDL_KEYDOWN){
                //cap/uncap the frame rate 'RETURN'
                if(event.key.keysym.sym == SDLK_RETURN) cap = (!cap);
                //restart/stop timer if user presses 'S'
                if(event.key.keysym.sym == SDLK_s){
                    if(myTimer.is_started()) myTimer.stop();
                    else myTimer.start();
                }
                //pause/unpause timer if user presses 'P'
                if(event.key.keysym.sym == SDLK_p){
                    if(myTimer.is_paused()) myTimer.unpause();
                    else myTimer.pause();
                }
            }
            //quick if the user Xes out the app
            if(event.type == SDL_QUIT) quit = true;
        }

        Uint8 *keystates = SDL_GetKeyState(NULL); //get keystate

        //If 'P' is pressed, increase alpha
        if(keystates[SDLK_p]){
            if(alpha < SDL_ALPHA_OPAQUE) alpha +=5;
        }

        //If 'L' is pressed, decrease alpha
         if(keystates[SDLK_l]){
            if(alpha > SDL_ALPHA_TRANSPARENT) alpha -=5;
        }

        //show dots if keys pressed
        if(keystates[SDLK_UP])apply_surface( 270, 0, dots, screen, &clip[0]);
        if(keystates[SDLK_DOWN])apply_surface( 270, 380, dots, screen, &clip[1]);
        if(keystates[SDLK_LEFT])apply_surface( 0, 190, dots, screen, &clip[2]);
        if(keystates[SDLK_RIGHT]) apply_surface( 540, 190, dots, screen, &clip[3]);

        //play sounds
        if(keystates[SDLK_1])Mix_PlayChannel(-1,scratch,0);
        if(keystates[SDLK_2])Mix_PlayChannel(-1,high,0);
        if(keystates[SDLK_3])Mix_PlayChannel(-1,med,0);
        if(keystates[SDLK_4])Mix_PlayChannel(-1,low,0);

        //start and stop the music
        if(keystates[SDLK_9]){
            if(Mix_PlayingMusic() ==0){
                if(Mix_PlayMusic(music,-1) == -1) return 1;
            }
            else if(Mix_PausedMusic() == 1) Mix_ResumeMusic();
            else Mix_PauseMusic();
        }

        if(keystates[SDLK_0]) Mix_HaltMusic(); //Stop the music

    //LOGIC
        myDot.move(myTimer.get_ticks());
        myDot.set_camera();

        //scroll background
        bgX -= 2;

        //reset background if it has gone too far
        if(bgX <= -background->w) bgX=0;

        //timer logic
        std::stringstream time;
        time << "Timer: " << myTimer.get_ticks() / 1000.f;
        seconds = TTF_RenderText_Solid(font, time.str().c_str(), textColor);

    //RENDERING

        //FILL THE SCREEN WHITE
        //SDL_FillRect( screen, &screen->clip_rect,
       // SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) );
        //apply_surface(240,190,foo,screen);

        //show background for moving camera
        //apply_surface(0,0,background,screen, &camera);

        //show moving background
        apply_surface(bgX,bgY,background,screen);
        apply_surface(bgX + background->w,bgY,background, screen);

        //show button
        myButton.show();

        //render timer
        apply_surface(350,50,seconds, screen);
        SDL_FreeSurface(seconds);

        //render the surface will alpha change
        SDL_SetAlpha(front, SDL_SRCALPHA, alpha);
        apply_surface(50,100,front, screen);

        //show dot
        myDot.show();

        if(SDL_Flip( screen ) == -1) return 1;   //Update Screen

        //increment the frame counter
        frame++;

        //cap the frame rate
        if(cap && (fps.get_ticks() < 1000 / FRAMES_PER_SECOND)){
            SDL_Delay((1000/FRAMES_PER_SECOND)- fps.get_ticks());
        }
    }

    clean_up();

    return 0;
}
*/
